﻿//#define BOX
#define CARS
#define WALL
//#define BALLS
//#define BALLSTACK
//#define ONEBALL
//#define CENTIPEDE
#define CANNON

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Skill.Ode;
using Skill.Ode.Joints;
using Skill.Ode.Geometries;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Samples.Demos
{
    public class Crash : OdeDemo
    {
        // select the method you want to test here (only uncomment *one* line)
        private const int QUICKSTEP = 1;

        //// some constants

        private const float LENGTH = 3.5f;		// chassis length
        private const float WIDTH = 2.5f;		// chassis width
        private const float HEIGHT = 1.0f;		// chassis height
        private const float RADIUS = 0.5f;		// wheel radius
        private const float STARTY = 1.0f;		// starting height of chassis
        private const float CMASS = 1;			// chassis mass
        private const float WMASS = 1;			// wheel mass
        private const float COMOFFSET = -5;		// center of mass offset
        private const float WALLMASS = 1;		// wall box mass
        private const float BALLMASS = 1;		// ball mass
        private const float FMAX = 25;			// car engine fmax
        private const float ROWS = 1;			// rows of cars
        private const float COLS = 1;			// columns of cars
        private const int ITERS = 20;		// number of iterations
        private const float WBOXSIZE = 1.0f;		// size of wall boxes
        private const float WALLWIDTH = 12;		// width of wall
        private const float WALLHEIGHT = 10;		// height of wall
        private const float DISABLE_THRESHOLD = 0.008f;	// maximum velocity (squared) a body can have and be disabled
        private const int DISABLE_STEPS = 10;	// number of steps a box has to have been disable-able before it will be disabled
        private const float CANNON_X = -10;		// x position of cannon
        private const float CANNON_Z = 5;		// y position of cannon
        private const float CANNON_BALL_MASS = 10f;	// mass of the cannon ball
        private const float CANNON_BALL_RADIUS = 0.5f;

        // dynamics and collision objects (chassis, 3 wheels, environment)

        private DWorld world;
        private DSpace space;
        private DBody[] body = new DBody[10000];
        private int bodies;
        private DHinge2[] joint = new DHinge2[100000];
        private int joints;
        private DJointGroup contactgroup;
        private DPlane ground;
        private DGeometry[] box = new DGeometry[10000];
        private int boxes;
        private DGeometry[] sphere = new DGeometry[10000];
        private int spheres;
        private DGeometry[] wall_boxes = new DGeometry[10000];
        private DBody[] wall_bodies = new DBody[10000];
        private DGeometry cannon_ball_geom;
        private DBody cannon_ball_body;
        private int[] wb_stepsdis = new int[10000];
        private int wb;
        private bool doFast;
        private DBody b;
        private DMass m;

        private DContactInfoArray contacts = new DContactInfoArray(4);
        private DNearCallback callback;

        // things that the user controls
        private float turn = 0, speed = 0;	// user commands
        private float cannon_angle = 0, cannon_elevation = -1.2f;



        // this is called by dSpaceCollide when two objects in space are
        // potentially colliding.

        private void NearCallback(object data, DSpace o1, DSpace o2)
        {
            int i, n;

            DBody b1 = ((DGeometry)o1).Body;
            DBody b2 = ((DGeometry)o2).Body;
            if (b1 != null && b2 != null && DJoint.AreConnected(b1, b2))
                return;

            n = DGeometry.Collide(o1, o2, 4, contacts);
            if (n > 0)
            {
                for (i = 0; i < n; i++)
                {
                    contacts[i].Surface.Mode = DSurfaceParametersMode.Slip1 | DSurfaceParametersMode.Slip2 | DSurfaceParametersMode.SoftERP |
                        DSurfaceParametersMode.SoftCFM | DSurfaceParametersMode.Approx1;
                    if (o1.ClassID == (int)OdeClassses.SphereClass || o2.ClassID == (int)OdeClassses.SphereClass)
                        contacts[i].Surface.Mu = 20f;
                    else
                        contacts[i].Surface.Mu = 0.5f;
                    contacts[i].Surface.Slip1 = 0.0f;
                    contacts[i].Surface.Slip2 = 0.0f;
                    contacts[i].Surface.SoftErp = 0.8f;
                    contacts[i].Surface.SoftCfm = 0.01f;
                    DContact c = new DContact(world, contactgroup, contacts[i]);
                    c.Attach(((DGeometry)o1).Body, ((DGeometry)o2).Body);
                }
            }
        }

        #region Constructor
        public Crash(DemoFramework framework)
            : base(framework)
        {
            callback = new DNearCallback(NearCallback);
        }
        #endregion

        private void MakeCar(float x, float z, ref int bodyI, ref int jointI, ref int boxI, ref int sphereI)
        {
            int i;
            DMass m = new DMass();

            // chassis body
            body[bodyI] = new DBody(world);
            body[bodyI].Position = new DVector3(x, STARTY, z);
            m.SetBox(1, LENGTH, HEIGHT, WIDTH);
            m.Adjust(CMASS / 2.0f);
            body[bodyI].Mass = m;
            box[boxI] = new DBox(space, LENGTH, HEIGHT, WIDTH);
            box[boxI].Body = body[bodyI];

            // wheel bodies
            DQuaternion q = DQuaternion.FromAxisAndAngle(1, 0, 0, MathHelper.PiOver2);
            m.SetSphere(1, RADIUS);
            m.Adjust(WMASS);
            for (i = 1; i <= 4; i++)
            {
                body[bodyI + i] = new DBody(world);
                body[bodyI + i].Quaternion = q;
                body[bodyI + i].Mass = m;
                sphere[sphereI + i - 1] = new DSphere(space, RADIUS);
                sphere[sphereI + i - 1].Body = body[bodyI + i];
            }
            body[bodyI + 1].Position = new DVector3(x + 0.4f * LENGTH - 0.5f * RADIUS, STARTY - HEIGHT * 0.5f, z + WIDTH * 0.5f);
            body[bodyI + 2].Position = new DVector3(x + 0.4f * LENGTH - 0.5f * RADIUS, STARTY - HEIGHT * 0.5f, z - WIDTH * 0.5f);
            body[bodyI + 3].Position = new DVector3(x - 0.4f * LENGTH + 0.5f * RADIUS, STARTY - HEIGHT * 0.5f, z + WIDTH * 0.5f);
            body[bodyI + 4].Position = new DVector3(x - 0.4f * LENGTH + 0.5f * RADIUS, STARTY - HEIGHT * 0.5f, z - WIDTH * 0.5f);

            // front and back wheel hinges
            for (i = 0; i < 4; i++)
            {
                joint[jointI + i] = new DHinge2(world, null);
                joint[jointI + i].Attach(body[bodyI], body[bodyI + i + 1]);
                DVector3 a = body[bodyI + i + 1].Position;
                joint[jointI + i].Anchor1 = a;
                joint[jointI + i].Axis1 = new DVector3(0, (i < 2 ? 1 : -1), 0);
                joint[jointI + i].Axis2 = new DVector3(0, 0, 1);
                joint[jointI + i].SetParam(DJointParameters.SuspensionERP, 0.8f);
                joint[jointI + i].SetParam(DJointParameters.SuspensionCFM, 1.0f / 100000f);
                joint[jointI + i].SetParam(DJointParameters.Velocity2, 0);
                joint[jointI + i].SetParam(DJointParameters.FMax2, FMAX);
            }

            //center of mass offset body. (hang another copy of the body COMOFFSET units below it by a fixed joint)
            DBody b = new DBody(world);
            b.Position = new DVector3(x, STARTY + COMOFFSET, z);
            m.SetBox(1, LENGTH, HEIGHT, WIDTH);
            m.Adjust(CMASS / 2.0f);
            b.Mass = m;
            DFixed j = new DFixed(world, null);
            j.Attach(body[bodyI], b);
            j.SetFixed();
            //box[boxI+1] = dCreateBox(space,LENGTH,WIDTH,HEIGHT);
            //dGeomSetBody (box[boxI+1],b);

            bodyI += 5;
            jointI += 4;
            boxI += 1;
            sphereI += 4;
        }


        void ResetSimulation()
        {
            int i;
            i = 0;
            // destroy world if it exists
            if (bodies > 0)
            {
                contactgroup.Dispose();
                space.Dispose();
                world.Dispose();
            }

            for (i = 0; i < 1000; i++)
                wb_stepsdis[i] = 0;

            // recreate world

            world = new DWorld();

            //	space = dHashSpaceCreate( 0 );
            //	space = dSimpleSpaceCreate( 0 );
            space = new DSweepAndPruneSpace(null, DSweepAndPruneSpace.SapAxes.XYZ);

            contactgroup = new DJointGroup();
            world.Gravity = new DVector3(0, -1.5f, 0);
            world.CFM = 1.0f / 100000;
            world.ERP = 0.8f;
            world.QuickStepNumIterations = ITERS;
            ground = new DPlane(space, 0, 1, 0, 0);
            DisplayBox displayGround = new DisplayBox(Framework.GraphicsDevice, Color.LightGray, Matrix.CreateTranslation(0, -0.05f, 0), new Vector3(100, 0.1f, 100));
            Framework.Add(displayGround);

            bodies = 0;
            joints = 0;
            boxes = 0;
            spheres = 0;
            wb = 0;

#if CARS
            for (float x = 0.0f; x < COLS * (LENGTH + RADIUS); x += LENGTH + RADIUS)
                for (float z = -((ROWS - 1) * (WIDTH / 2 + RADIUS)); z <= ((ROWS - 1) * (WIDTH / 2 + RADIUS)); z += WIDTH + RADIUS * 2)
                    MakeCar(x, z, ref bodies, ref joints, ref boxes, ref spheres);
#endif
#if WALL
            m = new DMass();
            bool offset = false;
            for (float y = WBOXSIZE / 2.0f; y <= WALLHEIGHT; y += WBOXSIZE)
            {
                offset = !offset;
                for (float z = (-WALLWIDTH + y) / 2; z <= (WALLWIDTH - y) / 2; z += WBOXSIZE)
                {
                    wall_bodies[wb] = new DBody(world);
                    wall_bodies[wb].Position = new DVector3(-20, y, z);
                    m.SetBox(1, WBOXSIZE, WBOXSIZE, WBOXSIZE);
                    m.Adjust(WALLMASS);
                    wall_bodies[wb].Mass = m;
                    wall_boxes[wb] = new DBox(space, WBOXSIZE, WBOXSIZE, WBOXSIZE);
                    wall_boxes[wb].Body = wall_bodies[wb];
                    //dBodyDisable(wall_bodies[wb++]);
                    wb++;
                }
            }
            //dMessage(0,"wall boxes: %i", wb);
#endif
#if BALLS
            for (dReal x = -7; x <= -4; x+=1)
                for (dReal y = -1.5; y <= 1.5; y+=1)
                    for (dReal z = 1; z <= 4; z+=1)
                    {
                        b = dBodyCreate (world);
                        dBodySetPosition (b,x*RADIUS*2,y*RADIUS*2,z*RADIUS*2);
                        dMassSetSphere (&m,1,RADIUS);
                        dMassAdjust (&m, BALLMASS);
                        dBodySetMass (b,&m);
                        sphere[spheres] = dCreateSphere (space,RADIUS);
                        dGeomSetBody (sphere[spheres++],b);
                    }
#endif
#if ONEBALL
            b = dBodyCreate (world);
            dBodySetPosition (b,0,0,2);
            dMassSetSphere (&m,1,RADIUS);
            dMassAdjust (&m, 1);
            dBodySetMass (b,&m);
            sphere[spheres] = dCreateSphere (space,RADIUS);
            dGeomSetBody (sphere[spheres++],b);
#endif
#if BALLSTACK
            for (dReal z = 1; z <= 6; z+=1)
            {
                b = dBodyCreate (world);
                dBodySetPosition (b,0,0,z*RADIUS*2);
                dMassSetSphere (&m,1,RADIUS);
                dMassAdjust (&m, 0.1);
                dBodySetMass (b,&m);
                sphere[spheres] = dCreateSphere (space,RADIUS);
                dGeomSetBody (sphere[spheres++],b);
            }
#endif
#if CENTIPEDE
            dBodyID lastb = 0;
            for (dReal y = 0; y < 10*LENGTH; y+=LENGTH+0.1)
            {
                // chassis body

                b = body[bodies] = dBodyCreate (world);
                dBodySetPosition (body[bodies],-15,y,STARTZ);
                dMassSetBox (&m,1,WIDTH,LENGTH,HEIGHT);
                dMassAdjust (&m,CMASS);
                dBodySetMass (body[bodies],&m);
                box[boxes] = dCreateBox (space,WIDTH,LENGTH,HEIGHT);
                dGeomSetBody (box[boxes++],body[bodies++]);

                for (dReal x = -17; x > -20; x-=RADIUS*2)
                {
                    body[bodies] = dBodyCreate (world);
                    dBodySetPosition(body[bodies], x, y, STARTZ);
                    dMassSetSphere(&m, 1, RADIUS);
                    dMassAdjust(&m, WMASS);
                    dBodySetMass(body[bodies], &m);
                    sphere[spheres] = dCreateSphere (space, RADIUS);
                    dGeomSetBody (sphere[spheres++], body[bodies]);

                    joint[joints] = dJointCreateHinge2 (world,0);
                    if (x == -17)
                        dJointAttach (joint[joints],b,body[bodies]);
                    else
                        dJointAttach (joint[joints],body[bodies-2],body[bodies]);
                    const dReal *a = dBodyGetPosition (body[bodies++]);
                    dJointSetHinge2Anchor (joint[joints],a[0],a[1],a[2]);
                    dJointSetHinge2Axis1 (joint[joints],0,0,1);
                    dJointSetHinge2Axis2 (joint[joints],1,0,0);
                    dJointSetHinge2Param (joint[joints],dParamSuspensionERP,1.0);
                    dJointSetHinge2Param (joint[joints],dParamSuspensionCFM,1e-5);
                    dJointSetHinge2Param (joint[joints],dParamLoStop,0);
                    dJointSetHinge2Param (joint[joints],dParamHiStop,0);
                    dJointSetHinge2Param (joint[joints],dParamVel2,-10.0);
                    dJointSetHinge2Param (joint[joints++],dParamFMax2,FMAX);

                    body[bodies] = dBodyCreate (world);
                    dBodySetPosition(body[bodies], -30 - x, y, STARTZ);
                    dMassSetSphere(&m, 1, RADIUS);
                    dMassAdjust(&m, WMASS);
                    dBodySetMass(body[bodies], &m);
                    sphere[spheres] = dCreateSphere (space, RADIUS);
                    dGeomSetBody (sphere[spheres++], body[bodies]);

                    joint[joints] = dJointCreateHinge2 (world,0);
                    if (x == -17)
                        dJointAttach (joint[joints],b,body[bodies]);
                    else
                        dJointAttach (joint[joints],body[bodies-2],body[bodies]);
                    const dReal *b = dBodyGetPosition (body[bodies++]);
                    dJointSetHinge2Anchor (joint[joints],b[0],b[1],b[2]);
                    dJointSetHinge2Axis1 (joint[joints],0,0,1);
                    dJointSetHinge2Axis2 (joint[joints],1,0,0);
                    dJointSetHinge2Param (joint[joints],dParamSuspensionERP,1.0);
                    dJointSetHinge2Param (joint[joints],dParamSuspensionCFM,1e-5);
                    dJointSetHinge2Param (joint[joints],dParamLoStop,0);
                    dJointSetHinge2Param (joint[joints],dParamHiStop,0);
                    dJointSetHinge2Param (joint[joints],dParamVel2,10.0);
                    dJointSetHinge2Param (joint[joints++],dParamFMax2,FMAX);
                }
                if (lastb)
                {
                    dJointID j = dJointCreateFixed(world,0);
                    dJointAttach (j, b, lastb);
                    dJointSetFixed(j);
                }
                lastb = b;
            }
#endif
#if BOX
            body[bodies] = dBodyCreate (world);
            dBodySetPosition (body[bodies],0,0,HEIGHT/2);
            dMassSetBox (&m,1,LENGTH,WIDTH,HEIGHT);
            dMassAdjust (&m, 1);
            dBodySetMass (body[bodies],&m);
            box[boxes] = dCreateBox (space,LENGTH,WIDTH,HEIGHT);
            dGeomSetBody (box[boxes++],body[bodies++]);	
#endif
#if CANNON
            cannon_ball_body = new DBody(world);
            cannon_ball_geom = new DSphere(space, CANNON_BALL_RADIUS);
            m.SetSphereTotal(CANNON_BALL_MASS, CANNON_BALL_RADIUS);
            cannon_ball_body.Mass = m;
            cannon_ball_geom.Body = cannon_ball_body;
            cannon_ball_body.Position = new DVector3(CANNON_X, CANNON_BALL_RADIUS, CANNON_Z);
#endif
        }

        //        static void command (int cmd)
        //{
        //    switch (cmd) {
        //    case 'a': case 'A':
        //        speed += 0.3;
        //        break;
        //    case 'z': case 'Z':
        //        speed -= 0.3;
        //        break;
        //    case ',':
        //        turn += 0.1;
        //        if (turn > 0.3)
        //            turn = 0.3;
        //        break;
        //    case '.':
        //        turn -= 0.1;
        //        if (turn < -0.3)
        //            turn = -0.3;
        //        break;
        //    case ' ':
        //        speed = 0;
        //        turn = 0;
        //        break;
        //    case 'f': case 'F':
        //        doFast = !doFast;
        //        break;
        //    case '+':
        //        dWorldSetAutoEnableDepthSF1 (world, dWorldGetAutoEnableDepthSF1 (world) + 1);
        //        break;
        //    case '-':
        //        dWorldSetAutoEnableDepthSF1 (world, dWorldGetAutoEnableDepthSF1 (world) - 1);
        //        break;
        //    case 'r': case 'R':
        //        resetSimulation();
        //        break;
        //    case '[':
        //        cannon_angle += 0.1;
        //        break;
        //    case ']':
        //        cannon_angle -= 0.1;
        //        break;
        //    case '1':
        //        cannon_elevation += 0.1;
        //        break;
        //    case '2':
        //        cannon_elevation -= 0.1;
        //        break;
        //    case 'x': case 'X': {
        //        dMatrix3 R2,R3,R4;
        //        dRFromAxisAndAngle (R2,0,0,1,cannon_angle);
        //        dRFromAxisAndAngle (R3,0,1,0,cannon_elevation);
        //        dMultiply0 (R4,R2,R3,3,3,3);
        //        dReal cpos[3] = {CANNON_X,CANNON_Y,1};
        //        for (int i=0; i<3; i++) cpos[i] += 3*R4[i*4+2];
        //        dBodySetPosition (cannon_ball_body,cpos[0],cpos[1],cpos[2]);
        //        dReal force = 10;
        //        dBodySetLinearVel (cannon_ball_body,force*R4[2],force*R4[6],force*R4[10]);
        //        dBodySetAngularVel (cannon_ball_body,0,0,0);
        //        break;
        //    }
        //    }
        //}


        public override void StepDemo(Microsoft.Xna.Framework.GameTime gameTime)
        {
            int i, j;

#if BOX
                    dBodyAddForce(body[bodies-1],lspeed,0,0);
#endif
            for (j = 0; j < joints; j++)
            {
                float curturn = joint[j].Angle1;
                //dMessage (0,"curturn %e, turn %e, vel %e", curturn, turn, (turn-curturn)*1.0);
                joint[j].SetParam(DJointParameters.Velocity, (turn - curturn) * 1.0f);
                joint[j].SetParam(DJointParameters.FMax, float.MaxValue);
                joint[j].SetParam(DJointParameters.Velocity2, speed);
                joint[j].SetParam(DJointParameters.FMax2, FMAX);
                joint[j].Body1.Enable();
                joint[j].Body2.Enable();
            }
            if (doFast)
            {
                space.NearCallback = callback;
                space.Collide(null);
#if (QUICKSTEP)
                        dWorldQuickStep (world,0.05);
#elif (STEPFAST)
                        dWorldStepFast1 (world,0.05,ITERS);
#endif
                contactgroup.Empty();
            }
            else
            {
                space.NearCallback = callback;
                space.Collide(null);
                world.Step(0.05f);
                contactgroup.Empty();
            }

            for (i = 0; i < wb; i++)
            {
                b = wall_boxes[i].Body;
                if (b.IsEnabled)
                {
                    bool disable = true;
                    DVector3 lvel = b.LinearVelocity;
                    float lspeed = lvel.X * lvel.X + lvel.Y * lvel.Y + lvel.Z * lvel.Z;
                    if (lspeed > DISABLE_THRESHOLD)
                        disable = false;
                    DVector3 avel = b.AngularVelocity;
                    float aspeed = avel[0] * avel[0] + avel[1] * avel[1] + avel[2] * avel[2];
                    if (aspeed > DISABLE_THRESHOLD)
                        disable = false;

                    if (disable)
                        wb_stepsdis[i]++;
                    else
                        wb_stepsdis[i] = 0;

                    if (wb_stepsdis[i] > DISABLE_STEPS)
                    {
                        b.Disable();
                        //dsSetColor(0.5,0.5,1);
                    }
                    //else
                    //dsSetColor(1,1,1);

                }
                //else
                //dsSetColor(0.4,0.4,0.4);
                //dVector3 ss;
                //dGeomBoxGetLengths (wall_boxes[i], ss);
                //dsDrawBox(dGeomGetPosition(wall_boxes[i]), dGeomGetRotation(wall_boxes[i]), ss);
            }

            //else
            //{
            //    for (i = 0; i < wb; i++)
            //    {
            //        b = dGeomGetBody(wall_boxes[i]);
            //        if (dBodyIsEnabled(b))
            //            dsSetColor(1,1,1);
            //        else
            //            dsSetColor(0.4,0.4,0.4);
            //        dVector3 ss;
            //        dGeomBoxGetLengths (wall_boxes[i], ss);
            //        dsDrawBox(dGeomGetPosition(wall_boxes[i]), dGeomGetRotation(wall_boxes[i]), ss);
            //    }
            //}

            //dsSetColor (0,1,1);
            //dReal sides[3] = {LENGTH,WIDTH,HEIGHT};
            //for (i = 0; i < boxes; i++)
            //    dsDrawBox (dGeomGetPosition(box[i]),dGeomGetRotation(box[i]),sides);
            //dsSetColor (1,1,1);
            //for (i=0; i< spheres; i++) dsDrawSphere (dGeomGetPosition(sphere[i]),
            //               dGeomGetRotation(sphere[i]),RADIUS);

            // draw the cannon
            //dsSetColor (1,1,0);
            //dMatrix3 R2,R3,R4;
            //dRFromAxisAndAngle (R2,0,0,1,cannon_angle);
            //dRFromAxisAndAngle (R3,0,1,0,cannon_elevation);
            //dMultiply0 (R4,R2,R3,3,3,3);
            //dReal cpos[3] = {CANNON_X,CANNON_Y,1};
            //dReal csides[3] = {2,2,2};
            //dsDrawBox (cpos,R2,csides);
            //for (i=0; i<3; i++) cpos[i] += 1.5*R4[i*4+2];
            //dsDrawCylinder (cpos,R4,3,0.5);

            //// draw the cannon ball
            //dsDrawSphere (dBodyGetPosition(cannon_ball_body),dBodyGetRotation(cannon_ball_body),
            //          CANNON_BALL_RADIUS);
        }

        public override void Initialize()
        {
            doFast = true;

            // setup pointers to drawstuff callback functions
            //dsFunctions fn;
            //fn.version = DS_VERSION;
            //fn.start = &start;
            //fn.step = &simLoop;
            //fn.command = &command;
            //fn.stop = 0;
            //fn.path_to_textures = DRAWSTUFF_TEXTURE_PATH;
            //if (argc == 2)
            //{
            //    fn.path_to_textures = argv[1];
            //}

            DOde.Initialize2(DOde.DInitializeOdeFlags.ManualThreadCleanup);
            DOde.AllocateDataForThread(DOde.DAllocateOdeDataFlags.MaskAll);

            bodies = 0;
            joints = 0;
            boxes = 0;
            spheres = 0;

            ResetSimulation();

            // run simulation
            //dsSimulationLoop(argc, argv, 352, 288, &fn);            
        }

        public override void Quit()
        {
            contactgroup.Dispose();
            space.Dispose();
            world.Dispose();
            DOde.Close();
        }
        public override string[] GetHelpString()
        {
            //printf ("Press:\t'a' to increase speed.\n"
            //"\t'z' to decrease speed.\n"
            //"\t',' to steer left.\n"
            //"\t'.' to steer right.\n"
            //"\t' ' to reset speed and steering.\n"
            //"\t'[' to turn the cannon left.\n"
            //"\t']' to turn the cannon right.\n"
            //"\t'1' to raise the cannon.\n"
            //"\t'2' to lower the cannon.\n"
            //"\t'x' to shoot from the cannon.\n"
            //"\t'f' to toggle fast step mode.\n"
            //"\t'+' to increase AutoEnableDepth.\n"
            //"\t'-' to decrease AutoEnableDepth.\n"
            //"\t'r' to reset simulation.\n");
            return null;
        }
    }
}
